//
//  iMBiPhoneViewServer.m
//  iMBiPhoneViewServer
//
//  Created by Eberhard Rensch on 14.08.08.
//  Copyright 2008 E.R.S. All rights reserved.
//
// Plugin for iMedia Browser
//
// iMedia Browser is based on code originally developed by Jason Terhorst,
// further developed for Sandvox by Greg Hulands, Dan Wood, and Terrence Talbot.
// Contributions have also been made by Matt Gough, Martin Wennerberg and others
// as indicated in source files.
// 
// The iMedia Browser Framework is licensed under the following terms:
// 
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in all or substantial portions of the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the following
// conditions:
// 
//	Redistributions of source code must retain the original terms stated here,
//	including this list of conditions, the disclaimer noted below, and the
//	following copyright notice: Copyright (c) 2008 by Pleasant Software
// 
//	Redistributions in binary form must include, in an end-user-visible manner,
//	e.g., About window, Acknowledgments window, or similar, the original
//	terms stated here, including this list of conditions, the disclaimer noted
//	below, and the aforementioned copyright notice.
// 
//	Neither the name of Pleasant Software, nor the names of
//	contributors to iMedia Browser may be used to endorse or promote products
//	derived from the Software without prior and express written permission from
//	Pleasant Software or individual contributors, as appropriate.
// 
// Disclaimer: THE SOFTWARE IS PROVIDED BY THE COPYRIGHT OWNER AND CONTRIBUTORS
// "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
// LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE,
// AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES, OR OTHER LIABILITY, WHETHER IN AN ACTION OF
// CONTRACT, TORT, OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION WITH, THE
// SOFTWARE OR THE USE OF, OR OTHER DEALINGS IN, THE SOFTWARE.
//

#import "iMBiPhoneViewServer.h"
#import "TarWrapper.h"

// The Bonjour application protocol, which must:
// 1) be no longer than 14 characters
// 2) contain only lower-case letters, digits, and hyphens
// 3) begin and end with lower-case letter or digit
// It should also be descriptive and human-readable
// See the following for more information:
// http://developer.apple.com/networking/bonjour/faq.html

#define kDefaultServiceIdentifier		@"imediaserver"

#define kStreamBufferSize 1024

@implementation iMBiPhoneViewServer
@synthesize serviceIdentifier;
@synthesize applicationIdentifier;
@synthesize applicationName;
@synthesize delegate;
@synthesize macAddress;

- (void) _showAlert:(NSString*)title
{
	UIAlertView* alertView = [[[UIAlertView alloc] initWithTitle:title message:NSLocalizedString(@"Check your networking configuration.", @"iMBiPhoneViewServer: Error") delegate:self cancelButtonTitle:NSLocalizedString(@"OK",@"") otherButtonTitles:nil] autorelease];
	[alertView show];
	[alertView autorelease];
}

- (id) init
{
	self = [super init];
	if (self != nil) {
		nonPersistantAuthentications = [NSMutableSet new];
		self.serviceIdentifier = kDefaultServiceIdentifier;
	}
	return self;
}

- (void) dealloc
{
	[_inStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
	[_inStream release];

	[_outStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
	[_outStream release];

	[_server release];

	[serviceIdentifier release];
	[macAddress release];
	[applicationIdentifier release];
	[applicationName release];
	[nonPersistantAuthentications release];
	[super dealloc];
}

- (BOOL)openConnectionWithTimeOut:(NSTimeInterval)timeout
{
	if(self.applicationName == nil)
		self.applicationName = [[[NSBundle mainBundle] localizedInfoDictionary] objectForKey:@"CFBundleDisplayName"];
	if(self.applicationName == nil)
		self.applicationName = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleDisplayName"];
	//NSLog(self.applicationIdentifier);
	//NSLog(self.applicationName);
	
	[_server release];
	_server = nil;
	
	[_inStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
	[_inStream release];
	_inStream = nil;
	_inReady = NO;

	[_outStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
	[_outStream release];
	_outStream = nil;
	_outReady = NO;
	
	_server = [TCPServer new];
	[_server setDelegate:self];
	NSError* error;
	if(_server == nil || ![_server start:&error]) {
		//NSLog(@"Failed creating server: %@", error);
		[self _showAlert:NSLocalizedString(@"Failed creating server", @"iMBiPhoneViewServer: Error")];
		return NO;
	}
	
	NSString* name = [NSString stringWithFormat:@"%@ (%@)",self.applicationName, [[UIDevice currentDevice] name]];
	//Start advertising to clients, passing nil for the name to tell Bonjour to pick use default name
	if(![_server enableBonjourWithDomain:@"local" applicationProtocol:[TCPServer bonjourTypeFromIdentifier:self.serviceIdentifier] name:name]) {
		[self _showAlert:NSLocalizedString(@"Failed advertising server", @"iMBiPhoneViewServer: Error")];
		return NO;
	}

	[timeoutTimer invalidate];
	if(timeout>0.)
	{
		timeoutTimer = [NSTimer scheduledTimerWithTimeInterval:timeout target:delegate selector:@selector(iMBiPhoneViewServerDisconnect) userInfo:nil repeats:NO];
	}
	else
		timeoutTimer=nil;
	
	return YES;
}

- (void) openStreams
{
	_inStream.delegate = self;
	[_inStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
	[_inStream open];
	_outStream.delegate = self;
	[_outStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
	[_outStream open];
}

- (void)closeStreams
{
	[_inStream close];
	[_outStream close];
	[_inStream release];
	[_outStream release];
	_inStream = nil;
	_outStream = nil;
}

- (void)closeConnection
{
	[timeoutTimer invalidate];

	[self closeStreams];
	[_server disableBonjour];
	[_server release];
	_server=nil;
	[[UIApplication sharedApplication] setIdleTimerDisabled:NO];
}

- (void)didAcceptConnectionForServer:(TCPServer*)server inputStream:(NSInputStream *)istr outputStream:(NSOutputStream *)ostr
{
	if(timeoutTimer)
	{
		[timeoutTimer invalidate];
		timeoutTimer=nil;
	}
	if (_inStream || _outStream || server != _server)
		return;
		
	srandomdev();
	authenticationCode = random()%9000 + 999;

	_inStream = istr;
	[_inStream retain];
	_outStream = ostr;
	[_outStream retain];
	
	[self openStreams];
	[[UIApplication sharedApplication] setIdleTimerDisabled:YES];
}

- (void)sendCommand:(NSData*)resonseData writePosition:(UInt32)position
{
	if([_outStream hasSpaceAvailable])
	{
		if(position==0)
		{
			UInt32 bytesToSend = CFSwapInt32HostToBig([resonseData length]);
			if([_outStream write:(const uint8_t *)&bytesToSend maxLength:sizeof(UInt32)]==-1)
			{
				//NSLog(@"Failed sending data length to peer");
				[delegate iMBiPhoneViewServerError:[NSError errorWithDomain:@"iMBiPhoneViewServer" code:1 userInfo:[NSDictionary dictionaryWithObject:@"Failed sending data length to peer" forKey:NSLocalizedDescriptionKey]]];
				return;
			}
		}
		
		NSUInteger bytesToWrite = [resonseData length]-position;
		NSInteger written = [_outStream write:[resonseData bytes]+position maxLength:bytesToWrite];
		if(written<=0)
		{
			//NSLog(@"Failed sending data to peer");
			[delegate iMBiPhoneViewServerError:[NSError errorWithDomain:@"iMBiPhoneViewServer" code:1 userInfo:[NSDictionary dictionaryWithObject:@"Failed sending data to peer" forKey:NSLocalizedDescriptionKey]]];
			return;
		}
		else
		{
			position += written;
			if(position<[resonseData length])
				[self sendCommand:resonseData writePosition:position];
		}
	}
	else if(_inReady && _outReady)
	{
		[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:.1]];
		[self sendCommand:resonseData writePosition:position];
	}
}

- (void)sendCommand:(NSDictionary*)command
{
	NSData* commandData = [(NSData *)CFPropertyListCreateXMLData(NULL, command) autorelease];
	[self sendCommand:commandData writePosition:0];
}

#pragma mark -

- (void)handleCommand
{
	NSAutoreleasePool* pool = [NSAutoreleasePool new];
	
	if([receivedData length]>0)
	{
		NSString* errorString=nil;
		NSDictionary* receivedCommand = [(NSDictionary*)CFPropertyListCreateFromXMLData (nil, (CFDataRef)receivedData, kCFPropertyListImmutable, (CFStringRef*)&errorString) autorelease];
		if(receivedCommand && !errorString)
		{
#pragma mark handle isAuthenticated
			// This handles a computer's request for authentication on the device
			if([[receivedCommand objectForKey:@"command"] isEqualToString:@"isAuthenticated"])
			{
				// Remember the computer's mac for later (in case 
				self.macAddress=[receivedCommand objectForKey:@"macAddress"];
				NSString* computerName = [receivedCommand objectForKey:@"computerName"];

				// Re-call iMBiPhoneViewServerConnectedTo, this time with a computer name
				[delegate iMBiPhoneViewServerConnectedTo:computerName];
				
				BOOL auth = (self.macAddress!=nil && 
					(	[nonPersistantAuthentications containsObject:self.macAddress] || 
						[[[NSUserDefaults standardUserDefaults] objectForKey:@"iMBiPhoneViewServerAuthenticatedClients"] containsObject:self.macAddress]) );
				[self setAuthenticationStatus:auth permanent:NO];
			}
#pragma mark handle catalog
			// Call delegate to create a iMediaParser compatible content array
			else if([[receivedCommand objectForKey:@"command"] isEqualToString:@"catalog"])
			{
				NSArray* catalog=[delegate iMBiPhoneViewServerCatalog];
				if(catalog)
				{
					[self sendCommand:[NSDictionary dictionaryWithObjectsAndKeys:
						@"catalog", @"command",
						catalog, @"content",
						nil]];
				}
				else
				{
					[self sendCommand:[NSDictionary dictionaryWithObjectsAndKeys:
						@"catalog", @"command",
						nil]];
				}
			}
#pragma mark handle fetchFile
			// Handle a computer's request for a file (or data), identified by a uuid
			// The delegate decides, if the file/data is send as one data chunk or as a file (multiple chunks)
			// To send data in one chunk, it has to be loaded completly into memory!
			else if([[receivedCommand objectForKey:@"command"] isEqualToString:@"fetchFile"])
			{
				id data = [delegate iMBiPhoneViewServerFetchFileForUUID:[receivedCommand objectForKey:@"uuid"]];
				if([data isKindOfClass:[NSData class]])
				{
					[self sendCommand:[NSDictionary dictionaryWithObjectsAndKeys:
						@"sendDataToComputer", @"command",
						data, @"data",
						nil]];
				}
				else if([data isKindOfClass:[NSString class]])
				{	// If data wasn't a NSData object, it's supposed to be NSString containing the file's path
					sendingFile = [[TarWrapper alloc] initForSendingWithSourcePath:(NSString*)data];
					if(sendingFile)
					{
						// This command should trigger computerRequestFileChunk requests or a sendFileToComputerResult response
						NSMutableDictionary* response = [NSMutableDictionary dictionaryWithObjectsAndKeys:
							@"sendFileToComputerResponse", @"command",
							[NSNumber numberWithBool:YES], @"success",
							nil];
							
						if([[receivedCommand objectForKey:@"requestSize"] boolValue])
						{
							NSNumber* totalSize = [sendingFile calculateTotalSize];
							if(totalSize)
								[response setObject:totalSize forKey:@"totalSize"];
						}
						[self sendCommand:response];
					}
					else
					{
						NSString* message = NSLocalizedString(@"Cannot open file to send",@"iMBiPhoneViewServer: Error");
						[delegate iMBiPhoneViewServerError:[NSError errorWithDomain:@"iMBiPhoneViewServer" code:1 userInfo:[NSDictionary dictionaryWithObject:message forKey:NSLocalizedDescriptionKey]]];
						[self sendCommand:[NSDictionary dictionaryWithObjectsAndKeys:
							@"sendFileToComputerResponse", @"command",
							[NSNumber numberWithBool:NO], @"success",
							message, @"message",
							nil]];
					}
				}
				else
				{
					NSString* message = NSLocalizedString(@"File/Data not found",@"iMBiPhoneViewServer: Error");
					[delegate iMBiPhoneViewServerError:[NSError errorWithDomain:@"iMBiPhoneViewServer" code:1 userInfo:[NSDictionary dictionaryWithObject:message forKey:NSLocalizedDescriptionKey]]];
					[self sendCommand:[NSDictionary dictionaryWithObjectsAndKeys:
						@"sendFileToComputerResponse", @"command",
						[NSNumber numberWithBool:NO], @"success",
						message, @"message",
						nil]];
				}
			}
#pragma mark handle sendFileToComputerResult
			else if([[receivedCommand objectForKey:@"command"] isEqualToString:@"sendFileToComputerResult"])
			{
				if([[receivedCommand objectForKey:@"success"] boolValue])
				{	// If success == YES, the file was delivered sucessfully
					[delegate iMBiPhoneViewServerSendFileToComputerSuccess];
				}
				else
				{	// Any other response is an error...
					if([receivedCommand objectForKey:@"message"])
						[delegate iMBiPhoneViewServerError:[NSError errorWithDomain:@"iMBiPhoneViewServer" code:1 userInfo:[NSDictionary dictionaryWithObject:[receivedCommand objectForKey:@"message"] forKey:NSLocalizedDescriptionKey]]];
					else
						[delegate iMBiPhoneViewServerError:nil];
				}
				
				// In either case: We're through with the sendingFile
				[sendingFile release];
				sendingFile=nil;
			}
#pragma mark handle sendDataToDevice
			else if([[receivedCommand objectForKey:@"command"] isEqualToString:@"sendDataToDevice"])
			{
				NSDictionary* result = [delegate iMBiPhoneViewServerReceivedDataFromComputer:receivedCommand];

				NSMutableDictionary* command = [NSMutableDictionary dictionaryWithObject:@"sendDataToDeviceResult" forKey:@"command"];
				if(result)
					[command addEntriesFromDictionary:result];
				else
					[command setObject:[NSNumber numberWithBool:NO] forKey:@"success"];
				[self sendCommand:command];
			}
#pragma mark handle sendFileToDevice
			else if([[receivedCommand objectForKey:@"command"] isEqualToString:@"sendFileToDevice"])
			{
				NSDictionary* result = [delegate iMBiPhoneViewServerReceiveFileFromComputerStart:receivedCommand];
				if([[result objectForKey:@"success"] boolValue])
				{
					NSString* destinationPath = [result objectForKey:@"destinationPath"];
					if(destinationPath==nil)
						destinationPath = [receivedCommand objectForKey:@"destinationPath"];
				
					//NSLog(@"Start Receiving File: %@", [destinationPath lastPathComponent]);
					receivingFile = [[TarWrapper alloc] initForReceivingWithDestinationPath:destinationPath];
					if(receivingFile)
					{
						if([[result objectForKey:@"dontUnwrap"] boolValue])
							[receivingFile setDontWrapData];
						[self sendCommand:[NSDictionary dictionaryWithObjectsAndKeys:
							@"deviceRequestsFileChunk", @"command",
							nil]];
					}
					else
					{
						NSString* message = NSLocalizedString(@"Cannot open file to receive", @"iMBiPhoneViewServer: Error");
						[delegate iMBiPhoneViewServerError:[NSError errorWithDomain:@"iMBiPhoneViewServer" code:1 userInfo:[NSDictionary dictionaryWithObject:message forKey:NSLocalizedDescriptionKey]]];
						[self sendCommand:[NSDictionary dictionaryWithObjectsAndKeys:
							@"sendFileToDeviceResult", @"command",
							[NSNumber numberWithBool:NO], @"success",
							message, @"message",
							nil]];
					}
				}
				else
				{
					NSMutableDictionary* command = [NSMutableDictionary dictionaryWithObject:@"sendFileToDeviceResult" forKey:@"command"];
					if(result)
						[command addEntriesFromDictionary:result];
					else
						[command setObject:[NSNumber numberWithBool:NO] forKey:@"success"];
					[self sendCommand:command];
				}
			}
#pragma mark handle sendFileChunkToDevice
			else if([[receivedCommand objectForKey:@"command"] isEqualToString:@"sendFileChunkToDevice"])
			{
				if(receivingFile && [receivingFile receivedData:[receivedCommand objectForKey:@"chunk"]])
				{
					//NSLog(@"Received %d bytes", [[receivedCommand objectForKey:@"chunk"] length]);
					[self sendCommand:[NSDictionary dictionaryWithObjectsAndKeys:
						@"deviceRequestsFileChunk", @"command",
						nil]];
				}
				else
				{
	
					NSString* message = [receivingFile lastErrorMessage];
					if(message==nil)
						message = NSLocalizedString(@"Unknown error in sendFileChunkToDevice",@"iMBiPhoneViewServer: Error");
					
					[receivingFile release];
					receivingFile=nil;
					[delegate iMBiPhoneViewServerError:[NSError errorWithDomain:@"iMBiPhoneViewServer" code:1 userInfo:[NSDictionary dictionaryWithObject:message forKey:NSLocalizedDescriptionKey]]];
					[self sendCommand:[NSDictionary dictionaryWithObjectsAndKeys:
						@"sendFileToDeviceResult", @"command",
						[NSNumber numberWithBool:NO], @"success",
						message, @"message",
						nil]];
				}
			}
#pragma mark handle sendFileToDeviceFinished
			else if([[receivedCommand objectForKey:@"command"] isEqualToString:@"sendFileToDeviceFinished"])
			{
				//NSLog(@"File complete");
				if([receivingFile flushReceiveBuffers])
				{
					[delegate iMBiPhoneViewServerReceiveFileFromComputerSuccess];
					[self sendCommand:[NSDictionary dictionaryWithObjectsAndKeys:
						@"sendFileToDeviceResult", @"command",
						[NSNumber numberWithBool:YES], @"success",
						nil]];
				}
				else
				{
					NSString* message = [receivingFile lastErrorMessage];
					if(message==nil)
						message = NSLocalizedString(@"Unknown error in sendFileToDeviceFinished",@"iMBiPhoneViewServer: Error");
					[delegate iMBiPhoneViewServerError:[NSError errorWithDomain:@"iMBiPhoneViewServer" code:1 userInfo:[NSDictionary dictionaryWithObject:message forKey:NSLocalizedDescriptionKey]]];
					[self sendCommand:[NSDictionary dictionaryWithObjectsAndKeys:
						@"sendFileToDeviceResult", @"command",
						[NSNumber numberWithBool:NO], @"success",
						nil]];
				}
				
				[receivingFile release];
				receivingFile=nil;
			}
#pragma mark handle sendDataToComputerResult
			else if([[receivedCommand objectForKey:@"command"] isEqualToString:@"sendDataToComputerResult"])
			{
				if([[receivedCommand objectForKey:@"success"] boolValue])
				{
					[delegate iMBiPhoneViewServerSendDataToComputerSuccess];
				}
				else
				{
					NSString* message = [receivedCommand objectForKey:@"message"];
					if(message)
						[delegate iMBiPhoneViewServerError:[NSError errorWithDomain:@"iMBiPhoneViewServer" code:1 userInfo:[NSDictionary dictionaryWithObject:message forKey:NSLocalizedDescriptionKey]]];
					else
						[delegate iMBiPhoneViewServerError:nil];
				}
			}
#pragma mark handle computerRequestsFileChunk
			else if([[receivedCommand objectForKey:@"command"] isEqualToString:@"computerRequestsFileChunk"])
			{
				NSData* chunk = [sendingFile dataToSend]; // Read a chunk of data
				if(chunk)	// send the chunk to the computer
				{
					[self sendCommand:[NSDictionary dictionaryWithObjectsAndKeys:
						@"sendFileChunkToComputer", @"command",
						chunk, @"chunk",
						nil]];
				}
				else // the file's end is reached, send sendFileToComputerFinished command to the computer
				{
					if([sendingFile lastErrorMessage])
					{
						[delegate iMBiPhoneViewServerError:[NSError errorWithDomain:@"iMBiPhoneViewServer" code:1 userInfo:[NSDictionary dictionaryWithObject:[sendingFile lastErrorMessage] forKey:NSLocalizedDescriptionKey]]];
						[self sendCommand:[NSDictionary dictionaryWithObjectsAndKeys:
							@"sendFileToComputerFinished", @"command",
							[NSNumber numberWithBool:NO], @"success",
							nil]];
					}
					else
					{
						[self sendCommand:[NSDictionary dictionaryWithObjectsAndKeys:
							@"sendFileToComputerFinished", @"command",
							[NSNumber numberWithBool:YES], @"success",
							nil]];
					}

					[sendingFile release];
					sendingFile=nil;
				}
			}
		}
		else
		{
			[delegate iMBiPhoneViewServerError:nil];
			[receivingFile release];
			receivingFile=nil;
			[sendingFile release];
			sendingFile=nil;
		}
	}
	[receivedData release];
	receivedData = nil;
	[pool release];
}

- (void) stream:(NSStream*)stream handleEvent:(NSStreamEvent)eventCode
{
	unsigned int len = 0;
	switch(eventCode)
	{
		case NSStreamEventOpenCompleted:
		{			
			if (stream == _inStream)
				_inReady = YES;
			else
				_outReady = YES;
			
			if (_inReady && _outReady)
			{
				[delegate iMBiPhoneViewServerConnectedTo:nil];
			}
			break;
		}
		case NSStreamEventHasBytesAvailable:
		{
			if(stream == _inStream)
			{
				if(!receivedData)
				{
					receivedData = [[NSMutableData data] retain];
					bytesToRead = 0;
					len = [_inStream read:(uint8_t*)&bytesToRead maxLength:sizeof(UInt32)];
					if(!len)
					{
						//NSLog(@"Failed reading data length from peer");
						[delegate iMBiPhoneViewServerError:[NSError errorWithDomain:@"iMBiPhoneViewServer" code:1 userInfo:[NSDictionary dictionaryWithObject:@"Failed reading data length from peer" forKey:NSLocalizedDescriptionKey]]];
						[receivedData release];
						receivedData = nil;
						bytesToRead = 0;
					}
					else
						bytesToRead = CFSwapInt32BigToHost(bytesToRead);
				}
				else if(bytesToRead>0)
				{
					uint8_t buf[kStreamBufferSize];
					UInt32 maxRead = MIN(kStreamBufferSize, (bytesToRead-[receivedData length]));
					len = [_inStream read:buf maxLength:maxRead];
					if(len) 
					{
						[receivedData appendBytes:(const void *)buf length:len];
					}
					else if([_inStream streamStatus] != NSStreamStatusAtEnd)
					{
						//NSLog(@"Failed reading data from peer");
						[delegate iMBiPhoneViewServerError:[NSError errorWithDomain:@"iMBiPhoneViewServer" code:1 userInfo:[NSDictionary dictionaryWithObject:@"Failed reading data from peer" forKey:NSLocalizedDescriptionKey]]];
						[receivedData release];
						receivedData = nil;
					}
				}
				if(bytesToRead>0 && receivedData && bytesToRead<=[receivedData length])
					[self handleCommand];
			}
			break;
		}
		case NSStreamEventErrorOccurred:
		{
			//NSLog(@"NSStreamEventErrorOccurred");
			[delegate iMBiPhoneViewServerError:[NSError errorWithDomain:@"iMBiPhoneViewServer" code:1 userInfo:[NSDictionary dictionaryWithObject:@"NSStreamEventErrorOccurred" forKey:NSLocalizedDescriptionKey]]];
			break;
		}
		case NSStreamEventEndEncountered:
		{			
			//NSLog(@"NSStreamEventEndEncountered");
			if (stream == _inStream)
				_inReady = NO;
			else
				_outReady = NO;
			if (!_inReady &&!_outReady)
				[self closeStreams];
			[delegate iMBiPhoneViewServerDisconnect];
			break;
		}
	}
}

- (void)setAuthenticationStatus:(BOOL)authenticated permanent:(BOOL)permanent
{
	if(authenticated)
	{
		if(permanent)
		{
			NSMutableArray* permaMAC = nil;
			NSArray* savedMAC = [[NSUserDefaults standardUserDefaults] objectForKey:@"iMBiPhoneViewServerAuthenticatedClients"];
			if(![savedMAC containsObject:self.macAddress])
			{
				if(savedMAC)
					permaMAC = [NSMutableArray arrayWithArray:savedMAC];
				else
					permaMAC = [NSMutableArray arrayWithCapacity:1];
				[permaMAC addObject:self.macAddress];
				[[NSUserDefaults standardUserDefaults] setObject:permaMAC forKey:@"iMBiPhoneViewServerAuthenticatedClients"];
			}
		}
	}
	if(authenticated)
	{
		[self sendCommand:[NSDictionary dictionaryWithObjectsAndKeys:
			@"authentication", @"command",
			[NSNumber numberWithBool:YES], @"isAuthenticated",
			nil]];
		[delegate iMBiPhoneViewServerAuthenticate:nil];
	}
	else
	{
		[self sendCommand:[NSDictionary dictionaryWithObjectsAndKeys:
			@"authentication", @"command",
			[NSNumber numberWithBool:NO], @"isAuthenticated",
			[NSString stringWithFormat:@"%04d",authenticationCode], @"authenticationCode",
			nil]];
		[delegate iMBiPhoneViewServerAuthenticate:[NSString stringWithFormat:@"%04d",authenticationCode]];
	}
}
@end
